import defaultData from './defaultData';
import { Graph, Node } from '@antv/x6';
import IndexdbHelper from './components/IndexdbHelper';
import { getNodeAttr } from './components/Lane/tools';
import {
  IndexStoreData,
  KeyValue,
  LinkData,
  NodeData,
  Point,
  ArchData,
  GraphData,
} from './types';

export const defaultModuleNodeName = 'moduleNode';
export const defaultEdgeShapeName = 'laneEdge';

export const dbHelper = new IndexdbHelper('arch_desgin', [
  {
    name: 'graphDatas',
    keyPath: 'id',
  },
]);

// // 自动保存数据
let autoSaveTimer: any;
export async function autoSaveJson(
  graphData: ArchData | null,
  graph?: Graph | null,
  onAutoSave?: (data: any) => void,
) {
  if (autoSaveTimer) clearTimeout(autoSaveTimer);
  const cache = (window as any)._useCache;
  if (!onAutoSave && !cache) return;
  return new Promise((resolve) => {
    autoSaveTimer = setTimeout(async () => {
      const data: IndexStoreData = {
        id: '1',
        updateTime: Date.now(),
        data: undefined,
        expired: (window as any)._saveInterval + 10000,
      };
      // 如果有 graphData 就强制保存 graphData
      if (graphData) {
        data.data = graphData;
      } else if (graph) {
        // 如果没有 graphData 有graph 就保存 graph 中的数据
        data.data = getGraphJSONData(graph);
      }
      onAutoSave && onAutoSave(data.data);
      // 如果都没有 data 为 undefined 将在下次恢复时直接使用 传入的数据
      if (!cache) return;
      await dbHelper.ready();
      await new Promise((resolve) => setTimeout(resolve, 100));
      const isExist = !!(await dbHelper.findByPk('graphDatas', '1'));
      if (isExist) {
        await dbHelper.update('graphDatas', data);
      } else {
        await dbHelper.create('graphDatas', data);
      }
      resolve(1);
    }, 100);
  });
}
// 甬道样式
export function getLaneStyle(index: number) {
  const nameRectFill = ['#EAFEEC', '#F9F7E6', '#E0EEFA', '#DFE5E6'];
  const nameTextFill = ['#6A8E67', '#978A64', '#495A7F', '#859092'];
  const bottomBorderStroke = ['#9BD787', '#E1BD81', '#7BA3FC', '#859092'];
  return {
    nameRect: {
      fill: nameRectFill[index],
    },
    nameText: {
      fill: nameTextFill[index],
    },
    bottomBorder: {
      stroke: bottomBorderStroke[index],
    },
  };
}
// 将原数据转换成画布数据
export function getGraphData(
  dataSource: ArchData,
  defaultEdgeShape: string,
): GraphData {
  if (!dataSource) return defaultData as any;
  const { lanes = [], nodes = [], links = [] } = dataSource;
  let lh = 25;
  let maxWidth = 0;
  const laneList = lanes.map((data: any, i: number) => {
    const { id, height, x, y, name, width, attrs } = data;
    lh += height;
    data.type = 'lane';
    data.sort = i;
    maxWidth = Math.max(maxWidth, width || 0);
    const laneData = {
      id,
      shape: 'lane',
      data,
      height,
      label: (name || '').split('').join('\n'),
      position: { x: x || 0, y: y || lh - height },
      attrs: attrs || getLaneStyle(i % 4),
      type: 'lane',
    };
    return laneData;
  });
  const nodeList = nodes.map((node) => createGraphNodeData(node));
  maxWidth = Math.max(
    maxWidth,
    ...nodeList.map((node: any) => (node.position.x || 0) + 200),
  );
  const linkList = links.map((link: any) => {
    const { id, source, target, shape } = link;
    link.type = 'link';
    return {
      id: id || `${source}_${target}`,
      shape: shape || defaultEdgeShape,
      source,
      target,
      data: link,
      type: 'link',
    };
  });
  return {
    width: maxWidth,
    height: lh + 25,
    laneList,
    nodeList,
    linkList,
  };
}

// 两行显示名称
/**
 * 用于实例详情图中任务名称显示
 * @param {string} label 需要处理的字符
 * @param {number} maxTextLen 最大长度 默认20
 * @param {number} maxLine  最大行数 默认1
 * @param {string} placehoder 中间占位字符 默认...
 */
export function getLabelText({
  label = '',
  placehoder = '...',
  maxTextLen = 20,
  maxLine = 2,
}) {
  if (!label) return '';
  const textList: any[] = [];
  let len = 0;
  let lineCount = 0;
  (label || '').split('').every((char) => {
    let crrLen;
    if (len === 0) textList[textList.length] = [];
    if (/[ilI]/.test(char)) crrLen = 0.5;
    else if (/[A-Z]/.test(char)) crrLen = 1.5;
    else if (/[^\u0000-\u00FF]/.test(char)) crrLen = 2;
    else crrLen = 1;
    if (
      lineCount === maxLine - 1 &&
      crrLen + len >= maxTextLen - placehoder.length
    ) {
      textList[textList.length - 1].push(placehoder);
      return false;
    }
    if (crrLen + len < maxTextLen + 1) {
      textList[textList.length - 1].push(char);
      len += crrLen;
    } else if (lineCount < maxLine) {
      textList[textList.length] = [char];
      len = crrLen;
      lineCount += 1;
    }
    return true;
  });
  return textList.map((list) => list.join('')).join('\n');
}

export function nextTick(callback: (...args: any) => void) {
  return Promise.resolve().then(callback);
}

export function getDndConfig(target: any) {
  return {
    target,
    // delegateGraphOptions
    validateNode: () => false,
    animation: true,
    getDragNode: () => {},
    getDropNode: () => {},
  };
}
// 将配置数据转换成节点所需数据
export function createGraphNodeData(
  nodeData: NodeData,
  config?: { graph: Graph; assets: KeyValue },
): Node.Metadata {
  const { graph, assets } = config || {};
  const {
    moduleType,
    name,
    x,
    y,
    preId,
    id: nodeId,
    shape,
    zIndex,
    ...rest
  } = nodeData;
  let id = nodeId;
  if (!id) {
    id = `${Date.now().toString(32)}_${Math.floor(
      Math.random() * 100000,
    ).toString(32)}`;
  }
  let position: Point = { x: 0, y: 0 };
  let attrs = {};
  if (x !== undefined && y !== undefined) {
    position = { x, y };
  } else if (graph && preId) {
    const lane = graph.getCellById(preId) as Node;
    const { x, y } = lane.position();
    position = { x: x + 60, y: y + 10 };
  }
  if (assets) {
    attrs = getNodeAttr(shape, nodeData, assets);
  }
  return {
    id,
    shape,
    position,
    parent: preId,
    type: 'node',
    zIndex: zIndex || 2,
    data: { moduleType, name, preId, id, ...rest, type: 'node' },
    attrs,
  };
}
// 将画布中的所有元素转换成 后端保存数据
export function getGraphJSONData(graph?: Graph): ArchData {
  const lanes: NodeData[] = [];
  const nodes: NodeData[] = [];
  const links: LinkData[] = [];
  if (!graph) {
    return {
      lanes,
      nodes,
      links,
    };
  }
  const { cells } = graph.toJSON();
  const { clientWidth, clientHeight } = graph.container;
  cells.forEach((cell) => {
    const {
      shape,
      position,
      size,
      data,
      parent,
      zIndex,
      source,
      target,
      type: cellType,
    } = cell;
    const type = cellType || data?.type;
    if (!type) return;
    if (type === 'link') {
      links.push({
        source: source.cell,
        target: target.cell,
        zIndex: zIndex || 2,
        shape,
        type,
      });
    } else {
      const node: NodeData = {
        ...data,
        ...size,
        ...position,
        preId: parent,
        type,
        shape,
      };
      if (type === 'lane') {
        node.zIndex = zIndex || 1;
        lanes.push(node);
      } else {
        node.zIndex = zIndex || 2;
        nodes.push(node);
      }
    }
  });
  return {
    width: clientWidth,
    height: clientHeight,
    lanes,
    nodes,
    links,
  };
}
export function launchFullScreen(element: any) {
  if (element.requestFullscreen) {
    element.requestFullscreen();
  } else if (element.mozRequestFullScreen) {
    element.mozRequestFullScreen();
  } else if (element.webkitRequestFullscreen) {
    element.webkitRequestFullscreen();
  } else if (element.msRequestFullscreen) {
    element.msRequestFullscreen();
  }
}

export function exitFullscreen() {
  if ((document as any).exitFullscreen) {
    (document as any).exitFullscreen();
  } else if ((document as any).mozCancelFullScreen) {
    (document as any).mozCancelFullScreen();
  } else if ((document as any).webkitExitFullscreen) {
    (document as any).webkitExitFullscreen();
  }
}

export function getKey() {
  return Date.now().toString(32) + '_' + (Math.random() * 10000).toFixed(0);
}
